package jgespres;

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.logging.*;
import javax.swing.table.*;

/**
 * Gestionnaire de planifications de Thémes.
 * 
 * @author guerfyld
 * @version 2.0
 */
public class JPlanningMkr extends JPanel {
    // Constantes
    private static final int MAXMNUROW = 10;
    private static final Color BCKGRD_DFT = Color.WHITE;
    private static final Color BCKGRD_SEP = Color.LIGHT_GRAY;
    private static final String NONE = jgespres.getResourceString("Planning.Menu.None");
    private static final String SEP  = jgespres.getResourceString("Planning.Menu.RowStatus.1");

    // Eléments de l'interface
    private TableColumnModel tcm;
    private JTable           jPlanTable;
    private JTable           jRowHeader;
    private JMenu            hlpMenu;
    private GridBagLayout    menuLayout;
    
    // Element à gerer
    private Planning plan;
    private Tables   tables;
    
    // Logger
    private final Logger logger = Logger.getLogger("jgespres");
      
    // Actions
    private class ThemeMenuItem extends JCheckBoxMenuItem implements ActionListener {
        int row;
        int col;
        int session;
        Theme Thm;
        
        public ThemeMenuItem(String text, boolean b, int row, int col, int session, Theme Thm) {
            super(text, b);
            this.row     = row;
            this.col     = col;
            this.session = session;
            this.Thm     = Thm;
            addActionListener(this);
        }

        // ActionListener implementation
        public void actionPerformed(ActionEvent evt) {
            // Execute action uniquement si pas déjà sélectionné
            if (getState()) {
                logger.log(Level.FINE, "Affectation Theme --> (row,col)="+row+", "+col+", session:"+session+", Thm:"+Thm);
                
                // Mise à jour du planning
                plan.setPostTheme(row, col, session, Thm);
            }
        }
    }

    private class HelperMenuItem extends JCheckBoxMenuItem implements ActionListener {
        int row;
        int col;
        Student Hlp;
        
        public HelperMenuItem(String text, boolean b, int row, int col, Student Hlp, int pos) {
            super(text, b);
            this.row = row;
            this.col = col;
            this.Hlp = Hlp;
            // Positionnement dans le menu
            GridBagConstraints cst = new GridBagConstraints();
            cst.gridx = pos / MAXMNUROW; // Col
            cst.gridy = pos % MAXMNUROW; // Row
            cst.anchor = GridBagConstraints.WEST;
            menuLayout.setConstraints(this, cst);
            
            // Les Aides ne sont pas sélectionnable si :
            // 1 - sont déjà affectés comme aide, ou 
            // 2 - ont un Théme affecté
            if (Hlp != null) {
                int hlpPos = plan.getSchoolClass().getStudentPos(Hlp.getId());
                PlanningPost Pst = (PlanningPost) plan.getValueAt(row, hlpPos);
                if (Pst != null) {
                    // Aide est déjà affecté
                    if (Pst.getStudentId() != Hlp.getId())
                        setEnabled(false);
                    // Aide a un Thème affecté
                    else if (Pst.getThemeId() != 0)
                        setEnabled(false);
                }
            }
            
            addActionListener(this);
        }

        // ActionListener implementation
        public void actionPerformed(ActionEvent evt) {
            // Execute action uniquement si pas déjà sélectionné
            if (getState()) {
                logger.log(Level.FINE, "Affectation Aide --> (row,col)="+row+", "+col+", Hlp:"+Hlp);
                
                // Mise à jour du planning
                plan.setPostHelper(row, col, Hlp);
            }
        }
    }

    private class CellStatusMenuItem extends JCheckBoxMenuItem implements ActionListener {
        final int row;
        final int col;
        final int status;
        
        public CellStatusMenuItem(String text, boolean b, int row, int col, int status) {
            super(text, b);
            this.row = row;
            this.col = col;
            this.status = status;
            addActionListener(this);
        }

        public void actionPerformed(ActionEvent evt) {
            logger.log(Level.FINE, "Affectation Status --> (row,col)="+row+", "+col+", status:"+status);
            // Mise à jour du planning
            if (tables.isStatusExpandable(status)) {
                int end = plan.getRowCount();
                // Execute action uniquement si pas déjà sélectionnée
                if (getState()) {
                    for (int r=row; r<end; r++)
                        plan.setPostStatus(r, col, status);
                }
                // Sinon annule status sélectionné
                else {
                    for (int r=row; r<end; r++)
                        plan.setPostStatus(r, col, 0);
                }
            }
            else
                // Execute action uniquement si pas déjà sélectionnée
                if (getState()) plan.setPostStatus(row, col, status);
        }
    }
    
    private class RowStatusMenuItem extends JCheckBoxMenuItem implements ActionListener {
        final int row;
        final int status;
        
        public RowStatusMenuItem(String text, boolean b, int row, int status) {
            super(text, b);
            this.row = row;
            this.status = status;
            addActionListener(this);
        }

        // ActionListener implementation
        public void actionPerformed(ActionEvent evt) {
            logger.log(Level.FINE, "Affectation Status ligne --> row="+row+", status:"+status);
            // Mise à jour du planning
            if (status == -1) 
                plan.setDateSeparator(row, !plan.isDateSeparator(row));
            else
                // Execute action uniquement si pas déjà sélectionnée
                if (getState()) plan.setDateStatus(row, status);
            
            // Rafraichi entête de ligne
            jRowHeader.createDefaultColumnsFromModel();
        }
    }

    private class CellRenderer extends JPanel implements TableCellRenderer {
        // Données à gerer
        boolean canceled;
        boolean separator;
        JLabel cellLabel;
        
        public CellRenderer() {
            cellLabel = new JLabel("", SwingConstants.CENTER);
            cellLabel.setFont(new java.awt.Font("", java.awt.Font.BOLD, 20));
            setLayout(new BorderLayout());
            add(cellLabel, BorderLayout.CENTER);
        }
        
        public void paintComponent( Graphics g ) {
            super.paintComponent( g );
            // Cas particulier : Barre oblique rouge
            if (canceled) {
                g.setColor(Color.RED);
                g.drawLine(0, getHeight()-1, getWidth()-1, 0);
                g.drawLine(0, getHeight(), getWidth(), 0);
                g.drawLine(1, getHeight(), getWidth(), 1);
            }
            // Cas particulier : Séparateur de dates
            if (separator) {
                g.setColor(BCKGRD_SEP);
                g.drawLine(0, 0, getWidth(), 0);
                g.drawLine(0, 1, getWidth(), 1);
                g.drawLine(0, 2, getWidth(), 2);
                g.drawLine(0, 3, getWidth(), 3);
            }
        }
        
        public Component getTableCellRendererComponent(JTable table, 
                Object value, boolean isSelected, boolean hasFocus, 
                int row, int column) {
            // Récupération infos Cellule
            PlanningPost Pst = (PlanningPost) value;
            PlanningPost HlpPst = plan.getHelperPost(row, column);
            int cellStatus = 0;
            Color cellColor = null;
            canceled = false;
            if (Pst != null) {
                // Status de cellule
                cellStatus = Pst.getStatus();
                // Cas particulier du status de cellule 1
                if (Pst.getStatus() == 1) {
                    canceled = true;
                }
                else if (cellStatus != 0) {
                    cellColor = tables.getStatusColor(1, cellStatus);
                }
            }
            else {
                // Status de ligne
                cellColor = tables.getStatusColor(2, plan.getDateStatus(row));
            }
            
            // Affectation couleur fond de cellule
            setBackground(BCKGRD_DFT);
            logger.log(Level.FINE, "CELL("+row+","+column+")="+Pst+"; STATUS="+cellStatus);
            if (cellColor != null) {
                setBackground(cellColor);
            }
            
            // Cas particuliers :
            // - Séparateur d'élève
            if (plan.isStudentSeparator(column))
                setBackground(BCKGRD_SEP);
            // - Séparateur de dates
            separator = plan.isDateSeparator(row);
            
            // Affectation libellé cellule
            if (HlpPst != null) {
                int thmId = HlpPst.getThemeId();
                if (thmId != 0) {
                    Theme Thm = plan.getThemesCatalog().getThemeById(thmId);
                    //Poste d'aide
                    cellLabel.setText(jgespres.getResourceString("Planning.Helper.Prefix")+ tables.getCellLabel(HlpPst.getSession(), Thm.getIdent()));
                    cellLabel.setForeground(tables.getCellBackground(HlpPst.getSession(), Thm.getIdent()));
                }
                else
                    cellLabel.setText(jgespres.NULL);
            }
            else if (Pst != null) {
                int thmId = Pst.getThemeId();
                if (thmId != 0) {
                    Theme Thm = plan.getThemesCatalog().getThemeById(thmId);
                    // Poste principal
                    cellLabel.setText(tables.getCellLabel(Pst.getSession(), Thm.getIdent()));
                    cellLabel.setForeground(tables.getCellForeground(Pst.getSession(), Thm.getIdent()));
                    setBackground(tables.getCellBackground(Pst.getSession(), Thm.getIdent()));
                }
                else
                    cellLabel.setText(jgespres.NULL);
            }
            else
                cellLabel.setText(jgespres.NULL);
                
            return this;
        }
    }
    
    /**
     * Constructor for objects of class JPlanningMaker
     * 
     * @param planning      Planning à gerer
     */
    public JPlanningMkr(Planning planning) {
        super();
        
        // Création des modéles
        plan = planning;
        tables = plan.getTables();
        createGUI();
    }
    
    private void createGUI() {
        java.awt.Font f = new java.awt.Font("Times New Roman", java.awt.Font.PLAIN, 16);
        // Définition Grille du Planning
        JScrollPane tablePane = new JScrollPane();
        // -> Table d'affectation
        jPlanTable = new JTable() {
            // Implemente des Info Bulles sur les cellules de la JTable
            public String getToolTipText(MouseEvent evt) {
                Point p = evt.getPoint();
                int col  = convertColumnIndexToModel(columnAtPoint(p));
                int row  = rowAtPoint(p);
                PlanningPost Pst = plan.getPost(row, col);
                String txt = ((Pst != null) && (Pst.getThemeId() != 0)) ? Pst.getStudentExtra() : null;
                return txt;
            }
        };
        jPlanTable.setAutoCreateColumnsFromModel(false);
        jPlanTable.setFont(f);
        jPlanTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        jPlanTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        jPlanTable.setRowHeight(40);
        jPlanTable.setModel(plan);
        final CellRenderer jCell = new CellRenderer();
        tcm = new DefaultTableColumnModel() {
            // Fixe largeur des colonnes de la table et CellRenderer des colonnes
            public void addColumn(TableColumn tc) {
                if (plan.getColumnName(getColumnCount()).equals(".")) {
                    tc.setMinWidth(0);
                    tc.setPreferredWidth(3);
                }
                else
                    tc.setPreferredWidth(40);
                tc.setCellRenderer( jCell );
                super.addColumn(tc);
            }  
        };
        jPlanTable.setColumnModel(tcm);
        jPlanTable.setTableHeader(new JTableHeader(tcm) {
            // Implemente des Info Bulles sur les Entête de colonnes de la Table
            public String getToolTipText(MouseEvent evt) {
                Point p = evt.getPoint();
                int index = columnModel.getColumnIndexAtX(p.x);
                try {
                    int realIndex = columnModel.getColumn(index).getModelIndex();
                    return plan.getSchoolClass().getStudent(realIndex).getName();
                }
                catch (Exception e) {
                    return jgespres.NULL;
                }
            }
        });
        jPlanTable.createDefaultColumnsFromModel();
        jPlanTable.addMouseListener( new MouseAdapter() {
            // Implemente menu contextuel
            public void mousePressed(MouseEvent e) {
                showPopup(e);
            }
            public void mouseReleased(MouseEvent e) {
                showPopup(e);
            }
        });
        tablePane.setViewportView(jPlanTable); // Les entêtes de colonne sont automatiquement affectées à columnHeader du JScrollPane
        // -> Table d'entête de lignes
        jRowHeader = new JTable();
        jRowHeader.setAutoCreateColumnsFromModel(false);
        jRowHeader.setFont(f);
        jRowHeader.setRowHeight( jPlanTable.getRowHeight() );
        jRowHeader.setPreferredScrollableViewportSize(new Dimension(90, 0));
        jRowHeader.setSelectionModel( jPlanTable.getSelectionModel() );
        jRowHeader.setModel(new DefaultTableModel() {
            private final DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
            public String getColumnName(int col) {
                return jgespres.getResourceString("Planning.Row") +
                    " \\ " + 
                    jgespres.getResourceString("Planning.Col");
            }
            public int getColumnCount() {
                return 1;
            }
            public int getRowCount() {
                return plan.getRowCount();
            }
            public boolean isCellEditable(int row, int col) {
                return false;
            }
            public Object getValueAt(int row, int col) { 
                try {
                    return df.format(plan.getThemesCatalog().getDate(row));
                }    
                catch(Exception e) {}

                return "#Error";
            }
        });
        jRowHeader.addMouseListener( new MouseAdapter() {
            // Implemente menu contextuel
            public void mousePressed(MouseEvent e) {
                showRowStatusMenu(e);
            }
            public void mouseReleased(MouseEvent e) {
                showRowStatusMenu(e);
            }
        });
        final TableCellRenderer DftRenderer = jRowHeader.getTableHeader().getDefaultRenderer();
        final TableCellRenderer HeaderRenderer = new TableCellRenderer() {
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                Component cpt = DftRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                cpt.setBackground(tables.getStatusColor(2, plan.getDateStatus(row)));
                return cpt;
            }
        };
        jRowHeader.setColumnModel(new DefaultTableColumnModel() {
            // Fixe largeur des colonnes de la table et CellRenderer des colonnes
            public void addColumn(TableColumn tc) {
                tc.setPreferredWidth(100);
                tc.setCellRenderer(HeaderRenderer);
                super.addColumn(tc);
            }  
        });
        jRowHeader.createDefaultColumnsFromModel();
        // rowHeader.getColumnModel().getColumn(0).setCellRenderer( rowHeader.getTableHeader().getDefaultRenderer() );
        tablePane.setRowHeaderView(jRowHeader);  // Affecte les entêtes de ligne à rowHeader du JScrollPane
        tablePane.setCorner(JScrollPane.UPPER_LEFT_CORNER, jRowHeader.getTableHeader()); //sert à avoir les entêtes pour les colonnes fixes
        plan.addTableModelListener(new TableModelListener() {
            // Rafraichir jRowHeader/jPlanTable si methode fireTableStructureChanged() appelée sur modèle de jPlanTable
            public void tableChanged(TableModelEvent e) {
                if (e.getFirstRow() == -1 && e.getLastRow() == -1) {
                    jRowHeader.createDefaultColumnsFromModel();
                    jPlanTable.createDefaultColumnsFromModel();
                }
            }
        });

        setLayout( new BorderLayout(1, 2) );
        add(tablePane, BorderLayout.CENTER);

        // Définition sous-menu contextuel des aides
        hlpMenu = new JMenu(jgespres.getResourceString("Planning.Menu.Helpers"));
        // Astuce permettant d'acceder au composant contenant les 'MenuItem'
        menuLayout = new GridBagLayout();
        JMenuItem jmi = new JMenuItem();
        hlpMenu.add( jmi );
        ((JPopupMenu) jmi.getParent()).setLayout(menuLayout);
    }
    
    /**
     * Rafraichissement affichage du planning
     */
    public void refresh() {
        // Regénération affichage Planning
        //((Planning)jPlanTable.getModel()).reload();
        logger.log(Level.INFO, "Regénération affichage Planning");
        if (plan.needReload()) {
            plan.load(false);
        }
        jRowHeader.createDefaultColumnsFromModel();
        jPlanTable.createDefaultColumnsFromModel();
    }

    /**
     * Affichage pop-up de gestion d'une ligne du planning
     * 
     * @param e     Evenement générateur
     */                
    private void showRowStatusMenu(MouseEvent e) {
        if (e.isPopupTrigger()) {
            // Determine ligne à traiter
            int row = e.getY() / jPlanTable.getRowHeight();
            logger.log(Level.FINE, "Click sur ligne " + row);

            // Récupération infos planning
            int rowSts = plan.getDateStatus(row);
            
            // Construction menu Pop up "Status ligne"
            logger.log(Level.INFO, "Création menu des status de date");
            JPopupMenu m = new JPopupMenu();
            int siz = tables.getNbStatus(2);
            for(int status=1; status<=siz; status++) {
                logger.log(Level.FINEST, "status=" + status);
                String lib = tables.getStatusLabel(2, status);
                m.add(new RowStatusMenuItem(lib, (rowSts == status), row, status));
            }
            m.addSeparator();
            m.add(new RowStatusMenuItem(NONE, (rowSts == 0), row, 0));
            m.addSeparator();
            m.add(new RowStatusMenuItem(SEP, plan.isDateSeparator(row), row, -1));
            
            // Show popup
            m.show(e.getComponent(), e.getX(), e.getY());
        }
    }

    /**
     * Affichage pop-up sur cellule planning
     * 
     * @param e     Evenement générateur
     */                
    private void showPopup(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON1)
            showThemesPopup(e);
        else if (e.getButton() == MouseEvent.BUTTON3)
            showCellStatusMenu(e);
    }
    
    /**
     * Affichage pop-up des themes au planning
     * 
     * @param e     Evenement générateur
     */                
    private void showThemesPopup(MouseEvent e) {
        if (e.getClickCount() == 2) {
            // Determine N° de colonne/ligne à traiter
            int col = tcm.getColumnIndexAtX(e.getX());
            int row = e.getY() / jPlanTable.getRowHeight();
            logger.log(Level.FINE, "Click sur colonne " + col + " et ligne " + row);
            
            // Ne pas afficher la popup si Poste d'aide OU séparateur d'élève
            if (plan.isHelperPost(row, col) || plan.isStudentSeparator(col))
                return;

            // Récupération Post du planning
            PlanningPost Pst = plan.getPost(row, col);
            
            // Ne pas afficher la popup si Poste avec status
            if ((Pst != null) && Pst.getStatus() != 0)
                return;

            // Récupération infos planning
            int thmId = (Pst != null) ? Pst.getThemeId() : 0;
            int pstSs = (Pst != null) ? Pst.getSession() : -1;
            logger.log(Level.FINE, "Poste trouvé en ["+col+","+row+"] : " + Pst);
            
            // Constrution menu Popup "Themes"
            logger.log(Level.FINE, "Création menu des thèmes");
            JPopupMenu m = new JPopupMenu();
            // Determine themes à traiter
            String[] sessions = tables.getSessions();
            Theme[] lstThm = plan.getThemesCatalog().getThemesDate(row);
            for(int s=0; s<9; s++) {
                if (sessions[s] == null && s != 0) break;
                // Affiche nom de la session en grisé
                if (sessions[s] != null) {
                    logger.log(Level.FINEST, "Session=" + sessions[s]);
                    if (s != 0) m.addSeparator();
                    JMenuItem titleSession = new JMenuItem(sessions[s]);
                    titleSession.setEnabled(false);
                    m.add(titleSession);
                }
                // Affiche liste des thèmes pour la session
                for(int i=0; i<lstThm.length; i++) {
                    Theme Thm = lstThm[i];
                    logger.log(Level.FINEST, "Theme=" + Thm);
                    String thmLabel = tables.getCellLabel(s, Thm.getIdent())
                        + " - " + Thm.getLabel() + " " + Thm.getLargeLabel();
                    m.add(new ThemeMenuItem(thmLabel, ((pstSs == s) && (thmId == Thm.getId())), row, col, s, Thm));
                }
            }
            m.addSeparator();
            m.add(new ThemeMenuItem(NONE, (thmId == 0), row, col, 0, null));

            // Constrution Popup "Aides"
            logger.log(Level.FINE, "Création sous-menu des aides");
            // Menu apparait que si un Theme est affecté et Elève appartient à un groupe
            Student Std = plan.getSchoolClass().getStudent(col);
            if (thmId != 0 && !Std.getGroup().equals("")) {
                hlpMenu.removeAll();

                // Affiche les Elève du groupe
                Student[] lstHlp = plan.getSchoolClass().getStudentsOrderedByThm( Std.getGroup() );
                logger.log(Level.FINE, "Groupe/len:"+Std.getGroup()+" / " + lstHlp.length);
                int hlpId = (Pst != null) ? Pst.getHelperId() : 0;
                int i=0;
                for(int pos=0; pos<lstHlp.length; pos++) {
                    Student Hlp = lstHlp[pos];
                    if (Hlp.getId() != Std.getId()) {
                        logger.log(Level.FINEST, "Aide=" + Hlp);
                        String hlpLabel = Hlp.getName() + " (" + plan.getCrossRef().getCount(Std.getId(), Hlp.getId()) +")";
                        hlpMenu.add(new HelperMenuItem(hlpLabel, (hlpId == Hlp.getId()), row, col, Hlp, i));
                        i++;
                    }
                }
                m.addSeparator();
                hlpMenu.add(new HelperMenuItem(NONE, (hlpId == 0), row, col, null, i));
                m.addSeparator();
                m.add(hlpMenu);
            }

            // Show popup
            m.show(e.getComponent(), e.getX(), e.getY());
        }        
    }

    /**
     * Affichage pop-up des status de cellule
     * 
     * @param e     Evenement générateur
     */                
    private void showCellStatusMenu(MouseEvent e) {
        if (e.getClickCount() == 1) {
            // Determine N° de colonne/ligne à traiter
            int col = tcm.getColumnIndexAtX(e.getX());
            int row = e.getY() / jPlanTable.getRowHeight();
            logger.log(Level.FINE, "Click sur colonne " + col + " et ligne " + row);
            
            // Ne pas afficher la popup si séparateur d'élève
            if (plan.isStudentSeparator(col))
                return;

            // Récupération infos planning
            PlanningPost Pst = plan.getPost(row, col);
            int celSts = (Pst != null) ? Pst.getStatus() : 0;
            logger.log(Level.FINE, "Poste trouvé en ["+col+","+row+"] : " + Pst);

            // Constrution Popup "Etats"
            logger.log(Level.FINE, "Création sous-menu des etats");
            JPopupMenu m = new JPopupMenu();
            int siz = tables.getNbStatus(1);
            for(int status=1; status<=siz; status++) {
                logger.log(Level.FINEST, "status=" + status);
                String lib = tables.getStatusLabel(1, status);
                m.add(new CellStatusMenuItem(lib, (celSts == status), row, col, status));
            }
            m.addSeparator();
            m.add(new CellStatusMenuItem(NONE, (celSts == 0), row, col, 0));

            // Show popup
            m.show(e.getComponent(), e.getX(), e.getY());
        }
    }
}
